package com.adan.search.service.impl;

import com.adan.base.model.PageParams;
import com.adan.search.dto.SearchCourseParamDto;
import com.adan.search.dto.SearchPageResultDto;
import com.adan.search.po.CourseIndex;
import com.adan.search.service.CourseSearchService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Mr.M
 * @version 1.0
 * @description 课程搜索service实现类
 * @date 2022/9/24 22:48
 */
@Slf4j
@Service
public class CourseSearchServiceImpl implements CourseSearchService {

    @Value("${elasticsearch.course.index}")
    private String courseIndexStore;
    @Value("${elasticsearch.course.source_fields}")
    private String sourceFields;

    @Autowired
    RestHighLevelClient client;

    @Override
    public SearchPageResultDto<CourseIndex> queryCoursePubIndex(PageParams pageParams, SearchCourseParamDto courseSearchParam) {
        // 构建查询对象
        SearchRequest searchRequest = new SearchRequest(courseIndexStore);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // source源字段过虑
        String[] sourceField = sourceFields.split(",");
        // 字段过滤条件
        sourceBuilder.fetchSource(sourceField, new String[]{});
        if (courseSearchParam == null) {
            courseSearchParam = new SearchCourseParamDto();
        }
        // 添加 must 条件关键字
        String keywords = courseSearchParam.getKeywords();
        if (StringUtils.isNotEmpty(keywords)) {
            // 匹配关键字
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "name", "description");
            // 设置匹配占比
            multiMatchQueryBuilder.minimumShouldMatch("70%");
            // 提升另个字段的权重
            multiMatchQueryBuilder.field("name", 10);
            boolQueryBuilder.must(multiMatchQueryBuilder);
        }
        // 添加过滤条件
        // 大分类
        String mt = courseSearchParam.getMt();
        if (StringUtils.isNotEmpty(mt)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("mtName", mt));
        }
        // 小分类
        String st = courseSearchParam.getSt();
        if (StringUtils.isNotEmpty(st)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("stName", st));
        }
        // 难度等级
        String grade = courseSearchParam.getGrade();
        if (StringUtils.isNotEmpty(grade)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("grade", grade));
        }
        // 分页
        Long pageNo = pageParams.getPageNo();
        Long pageSize = pageParams.getPageSize();
        int start = (int) ((pageNo - 1) * pageSize);
        sourceBuilder.from(start);
        sourceBuilder.size(Math.toIntExact(pageSize));
        // 布尔查询
        sourceBuilder.query(boolQueryBuilder);
        // 高亮设置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font class='eslight'>");
        highlightBuilder.postTags("</font>");
        // 设置高亮字段
        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
        sourceBuilder.highlighter(highlightBuilder);
        // 请求搜索
        searchRequest.source(sourceBuilder);
        // 聚合设置
        buildAggregation(searchRequest);
        // 查询文档
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("课程搜索异常：{}", e.getMessage());
            return new SearchPageResultDto<CourseIndex>(new ArrayList(), 0, 0, 0);
        }

        // 结果集处理
        SearchHits hits = searchResponse.getHits();
        // 记录总数
        long totalHits = hits.getTotalHits().value;
        //数据列表
        List<CourseIndex> list = new ArrayList<>();
        // 文档数组
        SearchHit[] searchHits = hits.getHits();
        // 遍历文档
        for (SearchHit hit : searchHits) {
            // 获取文档source
            String json = hit.getSourceAsString();
            // 获取文档对象
            CourseIndex courseIndex = JSON.parseObject(json, CourseIndex.class);

            //取出sourceAsMap
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();

            //取出高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)){
                //取出高亮字段内容
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null){
                    /*Text[] fragments = highlightField.getFragments();
                    StringBuffer stringBuffer = new StringBuffer();
                    for (Text str : fragments) {
                        stringBuffer.append(str.string());
                    }
                    name = stringBuffer.toString();*/
                    // 取出高亮结果数组中的第一个数据
                    String name = highlightField.getFragments()[0].toString();
                    // 用高亮的结果替换courseIndex中的非高亮结果
                    courseIndex.setName(name);
                }
            }
            list.add(courseIndex);
        }
        // 封装返回数据
        SearchPageResultDto<CourseIndex> pageResult = new SearchPageResultDto<>(list, totalHits, pageNo, pageSize);
        // 解析聚合结果
        Aggregations aggregations = searchResponse.getAggregations();
        List<String> mtList= getAggregation(aggregations, "mtAgg");
        List<String> stList = getAggregation(aggregations, "stAgg");

        pageResult.setMtList(mtList);
        pageResult.setStList(stList);
        return pageResult;
    }

    /**
     * 根据聚合名称获取聚合结果
     * @param aggregations
     * @param aggName
     * @return
     */
    private List<String> getAggregation(Aggregations aggregations, String aggName) {
        // 封装返回数据
        List<String> list = new ArrayList<>();
        // 根据聚合名称获取聚合结果
        Terms terms = aggregations.get(aggName);
        // 获取buckets
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        // 遍历buckets
        for (Terms.Bucket bucket : buckets) {
            // 获取key
            String key = bucket.getKeyAsString();
            list.add(key);
        }
        return list;
    }

    private void buildAggregation(SearchRequest request) {
        request.source().aggregation(AggregationBuilders
                // 聚合类型、名称
                .terms("mtAgg")
                // 聚合字段
                .field("mtName")
                // 获取的聚合结果数量
                .size(100)
        );
        request.source().aggregation(AggregationBuilders
                .terms("stAgg")
                .field("stName")
                .size(100)
        );
    }

}
